Image

Setup Symfony 6 with ApiPlatform GraphQL, Rest API & TS ReactApp

19. Oktober 2022

Share on:

This is a guide on how to set up a GraphQL and REST Api with Symfony 6 and Api Platform, providing you with Swagger integration for the REST Api and Playground & GraphiQL for the GraphQL Api. Additionally we will set up a frontend app using ReactJS, Apollo and GraphQL codegen to automatically generate types from our GraphQL Api and hooks from our queries. We will mainly highlight the GraphQL integration as it requires extra steps, yet this setup provides a running REST api as well. In the end you will have a running dev setup to develop your API and web app.

Requirements

The requirements for the dev environment are:

  • PHP >=8.1 (enable postgres module)
  • Composer
  • Docker
  • symfony-cli
  • NodeJS >= 14 & npm >=5.6 (for the frontend only)

Symfony App Setup

I recommend setup via symfony-cli, whose installation depends on your OS.
You can find the different installation methods (also for different linux operating systems) on the symfony website.

After installing symfony-cli we are ready to setup our app:

symfony new test-api
cd test-api

symfony composer require api # generate recipes -> yes
composer require symfony/orm-pack # generate recipes -> yes
composer require --dev symfony/maker-bundle

# start db
docker-compose build --pull --no-cache
docker-compose up -d

symfony console doctrine:database:create
symfony console doctrine:schema:create

GOOD TO KNOW

  • If you are having trouble connecting to the database, make sure postgres is enabled in your php.ini
  • You can also run docker-compose ps to check if the db container is up and running after the docker-compose up

Creating & changing Entities

First, run php bin/console make:entity to generate an entity.
Next, run php bin/console make:migration to create a migration for your new entity and afterwards php bin/console doctrine:migrations:migrate to update your database.

You do not have to use make:entity, as to create an entity, you can add the file yourself. However, if you are not familiar with doctrine, I recommend you generate your first file as it will add all the required annotations.
Generally whenever you make changes to your entities you will have to create a migration and run it to update your database! You can also create your own migrations if you need to migrate actual data.
For learning more about Doctrine and migrations read here.

For your entity to be exposed via your apis it should have

#[ApiResource]

as annotation right above the class declaration. If you used the make command, this annotation should have been added automatically.
For learning more about API Platform start here.
If you want to learn about customizing what to expose in your entity read here.

Now we are ready to start our webserver:

# start api
symfony serve -d

# ouput db & api logs
docker-compose logs

# If your db container is not running anymore run:
docker-compose up -d

After setting everything up, you can access your api docs:
GraphQl GraphiQl Editor:
http://127.0.0.1:8000/api/graphql
GraphQl Playground:
http://127.0.0.1:8000/api/graphql/graphql_playground
Rest Api Swagger:
http://127.0.0.1:8000/api/

In order to start your server in the future and continue working, proceed as follows:

docker-compose up -d
symfony serve -d
docker-compose logs

GOOD TO KNOW

Keep in mind that the internal symfony web server is for the dev environment only! Deployments and environments are not covered in this article, but you can read about them here.

React App Setup

First, make sure you have NodeJS and npm installed and run:

npx create-react-app test-app --template typescript
npm install @apollo/client graphql
npm install @graphql-codegen/cli
npm install --save-dev apollo @types/graphql

GOOD TO KNOW

If you decide to not use GraphQL, you can skip the installation of the GraphQL packages and the rest of this article and fetch your data via the REST Api right away! Check your swagger endpoint for the required URLs!

 

Second, we need to set up Apollo, which will provide us with a store and easy handling of GraphQL queries.
Add the following code to your index.tsx and make sure to wrap your app with the ApolloProvider.

import {
ApolloClient,
InMemoryCache,
ApolloProvider,
useQuery,
gql
} from "@apollo/client";

const client = new ApolloClient({
uri: 'http://127.0.0.1:8000/api/graphql',
cache: new InMemoryCache()
});

//...
// Wrap app inside render in ApolloProvider and pass client
<ApolloProvider client={client}>
<App />
</ApolloProvider>

Now we will set up codegen by adding codegen.yml in our root. It should look like this (add withHooks):

overwrite: true
schema: "http://localhost:8000/api/graphql"
documents: './src/components/**/*.ts'
generates:
src/generated/graphql.tsx:
plugins:
- 'typescript'
- 'typescript-operations'
- 'typescript-react-apollo'
config:
withHooks: true

GOOD TO KNOW

You can also run: npm run graphql-codegen init to generate a codegen.yml, however you will still have to adjust it to the config above!
If you want to learn more about graphql codegen read here.

 

Next, we should make our first query to an entity we created. In this case it is an entity called session and we intend to fetch it by id:

components/session/query.ts

import { gql } from '@apollo/client';

export const getSession = gql`
query SessionQuery($id: ID!) {
session(id: $id) {
name
description
}
}
`;

GOOD TO KNOW

  • You can browse your playground or grapiql to see what queries are available
  • session is just the exemplary entity I used for this demo, name the file and query after whatever entity you want to query

 

Subsequently, we want to generate types and hooks for our new query:

npm run codegen

GOOD TO KNOW

The codegen command does a pretty good job at validating your queries and will give you precise errors regarding mistakes in your queries. If you are not experienced with GraphQL, it’s a big help!

 

After your codegen command has run successfully, it has generated hooks and types in
src/generated/graphql.tsx

Finally, let’s use the hook that was generated from our query:
components/session/session.tsx:

// inside component
const {data, loading, error} = useGetSessionQuery({variables: {id: 'api/sessions/' + props.id}});

// do something with the data in useEffect
useEffect(() => {
console.log(data);
}, [data]);
// same way u can listen to error, to handle errors

The useEffect will log the data whenever it is fetched or updated. You can use error as a dependency in another useEffect to listen to it.
It is worth looking into your generated graphql.tsx to see the different hooks that were generated and also which types are provided.

Conclusion

You are now ready to extend your API with more entities and develop your frontend app fetching the data via GraphQL!

To learn more about Apollo, it’s storage and what other queries you can use, you can start reading here.

Regarding the available GraphQL queries, you can also add your own custom queries on the backend side.