skip to Main Content

I fetch my Symfony API on React, and it takes 2 seconds to get and display the data even though there are only 3 lines.

How can I optimize time?

video: https://imgur.com/a/iGaFW0L

During the video recording, it takes a little longer, that’s how it is, but a bit faster

Symfony API

#[Route('/api/projects', methods: ['GET'], name: 'project_get')]
    public function getProjects(ProjectRepository $projectRepository): JsonResponse
    {
        return $this->json(
            [
                "status" => 200,
                "success" => true,
                "data" => $projectRepository->findAll(),
                "message" => "Operation completed with success"
            ], 
            Response::HTTP_OK, [], ['groups' => 'getProjects']
        );
    }

Fetch on React

useEffect(() => {
        fetch("https://localhost:8000/api/projects")
        .then((res) => res.json())
        .then((projects) => {
          console.log("projects",projects.data);
          setProjectsFromApi(projects.data);
          setLoading(false);
        })
    }, [])

networkapisymfony
symfonyprofiler
symfonyprofilerdoctrine

If I replace data with an empty array

#[Route('/api/projects', methods: ['GET'], name: 'project_get')]
    public function getProjects(ProjectRepository $projectRepository): JsonResponse
    {
        return $this->json(
            [
                "status" => 200,
                "success" => true,
                "data" => [],
                "message" => "Operation completed with success"
            ], 
            Response::HTTP_OK, [], ['groups' => 'getProjects']
        );
    }

networkapisymfonydata[]

symfonyprofilerdata[]

For comparison, I have a Nodejs API that is much faster, and there are not many fewer lines

nodejsapinetwork

2

Answers


  1. Chosen as BEST ANSWER

    With this code

    #[Route('/api/projects', methods: ['GET'], name: 'project_get')]
        public function getProjects(ProjectRepository $projectRepository): JsonResponse
        {
            $projects = $projectRepository->findAll();
    
            $response = array_map(
                fn (Project $project) => [
                    'id' => $project->getId(),
                    'title' => $project->getTitle(),
                    'content' => $project->getContent(),
                    'frameworks' => $project->getFrameworks()->map(
                        fn (Framework $framework) => [
                            'id' => $framework->getId(),
                            'name' => $framework->getName(),
                        ]
                    ),
                    'languages' => $project->getLanguages()->map(
                        fn (Language $language) => [
                            'id' => $language->getId(),
                            'name' => $language->getName(),
                        ]
                    )
                    ->toArray(),
                ],
                $projects
            );
    
            return $this->json(
                [
                    "status" => 200,
                    "success" => true,
                    "data" => $response,
                    "message" => "Operation completed with success"
                ], 
                Response::HTTP_OK
            );
        }
    

    It's much faster

    enter image description here


  2. One thing that you might try is not serializing whole entities, but specifying exactly, which items from an entity should be serialized. This is after all a best practice. Serializing entities, especially those with relations (in your case I suppose it is Project entity), may lead to poor performance or infinite recursion.

    Now, how can you control, which attributes to serialize?
    The fastest option is to simply loop through the projects returned by your repository and select, how the final response shouls look like. The snippet below contains some hints on how to do that.

            $projects = $this->projectRepository->findAll();
    
            $response = array_map(
                fn (Project $project) => [
                    'id' => $project->getId(),
                    'name' => $project->getName(),
                    'content' => $project->getContent(),
                    'frameworks' => $project->getFrameworks()->map(
                        fn (Framework $framework) => [
                            'id' => $framework->getId(),
                            'name' => $framework->getName(),
                        ]
                    )->toArray(),
                ],
                $projects
            );
    

    Then just pass the response into your data attribute in JSON. Just be sure not to include relations.

    If this improves the performance, you should have a look at Symfony Serializer and perhaps some DTO’s, so you can control, how your responses look like and control precisely how the mapping is done.

    If you are looking to further optimize performance, you could write your own query to fetch the projects and their framework in one query. As you can see in your example, the relations are loaded by Doctrine proxies, that are by default lazy. That means the relation is not loaded until it is needed. This could also hinder performance and lead to the infamous N+1 Query problem.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search