365 words
2 minutes
Create a Simple API with Symfony

With JavaScript frameworks, there’s an increasing need to access data via an API, without necessarily bringing out the heavy artillery (such as the excellent API Platform).

The API we will develop will be ultra-simple (no authentication, minimal error handling, no versioning, …).

It will include 5 routes to manage books:

  • GET: /api/book/{id}: Retrieve a single book.
  • GET: /api/books: Retrieve all books.
  • POST: /api/book: Create a book.
  • PUT: /api/book/{id}: Update a book.
  • DELETE: /api/book/{id}: Delete a book.

Project Creation#

To start, create a new Symfony project with PHP 8 and Symfony 6:

symfony new custom-api
symfony server:start

Database#

Add the necessary components to manage a database and install the Maker Bundle:

composer require orm
composer require --dev maker-bundle

Configure your connection in the .env.local file:

DATABASE_URL="mysql://login:password@localhost:3306/customApi?serverVersion=5.7"

Create an entity to represent a book:

bin/console make:entity Book

Update the database:

bin/console doctrine:database:create
bin/console make:migration
bin/console doctrine:migrations:migrate

Fixtures#

To generate sample data:

composer require --dev orm-fixtures
composer require fzaninotto/faker --dev
bin/console make:fixtures

Example of fixtures for 50 books:

class BookFixtures extends Fixture
{
    public function load(ObjectManager $manager): void
    {
        $faker = \Faker\Factory::create('en_US');

        for ($i = 0; $i < 50; $i++) {
            $book = new Book();
            $book->setAuthor($faker->firstName . ' ' . $faker->lastName);
            $book->setTitle($faker->realText($faker->numberBetween(10, 100)));
            $book->setPageCount(rand(50, 999));
            $manager->persist($book);
        }

        $manager->flush();
    }
}

Load the fixtures:

bin/console doctrine:fixtures:load

GET Routes#

Create a controller to manage books:

bin/console make:controller BookController

Add a route to retrieve a specific book:

#[Route('/api/book/{id}', name: 'get_book', methods: ['GET'])]
public function get($id): Response
{
    $book = $this->entityManager->getRepository(Book::class)->find($id);
    if (!$book) {
        return $this->json(null, Response::HTTP_NOT_FOUND);
    }

    return $this->json($book, Response::HTTP_OK);
}

Add another route to retrieve all books:

#[Route('/api/books', name: 'get_books', methods: ['GET'])]
public function getAll(): Response
{
    $books = $this->entityManager->getRepository(Book::class)->findAll();
    return $this->json($books, Response::HTTP_OK);
}

POST, PUT, and DELETE Routes#

To create a book:

#[Route('/api/book', name: 'add_book', methods: ['POST'])]
public function add(Request $request): Response
{
    $data = json_decode($request->getContent(), true);
    $book = new Book();
    $book->setTitle($data['title'])->setAuthor($data['author'])->setPageCount($data['pageCount']);

    $this->entityManager->persist($book);
    $this->entityManager->flush();

    return $this->json(['message' => 'Book created'], Response::HTTP_CREATED);
}

To update a book:

#[Route('/api/book/{id}', name: 'update_book', methods: ['PUT'])]
public function update(Request $request, $id): Response
{
    $book = $this->entityManager->getRepository(Book::class)->find($id);
    if (!$book) {
        return $this->json(null, Response::HTTP_NOT_FOUND);
    }

    $data = json_decode($request->getContent(), true);
    $book->setTitle($data['title'] ?? $book->getTitle())
         ->setAuthor($data['author'] ?? $book->getAuthor())
         ->setPageCount($data['pageCount'] ?? $book->getPageCount());

    $this->entityManager->flush();

    return $this->json($book, Response::HTTP_OK);
}

To delete a book:

#[Route('/api/book/{id}', name: 'delete_book', methods: ['DELETE'])]
public function delete($id): Response
{
    $book = $this->entityManager->getRepository(Book::class)->find($id);
    if ($book) {
        $this->entityManager->remove($book);
        $this->entityManager->flush();
    }

    return $this->json(null, Response::HTTP_NO_CONTENT);
}

Conclusion#

You have just created a simple API with Symfony. The complete source code is available on my GitHub.