How to call default API endpoint in custom plugin?

System Information
  • Strapi Version: 4.10.2
  • Operating System: Windows 11
  • Database: SQLite
  • Node Version: v18.15.0
  • NPM Version: 9.5.0
  • Yarn Version: 1.22.19

Hi all, I’ve found a similar query to mine on stackoverflow which doesn’t seem to have any working answers:

I wasn’t able to find any information on how to do this in the Strapi documentation either. Anyways, here is the issue at hand:

I’ve created a new custom plugin using the yarn strapi generate plugin command, and the following bits were generated:

src/plugins/my-plugin/server/routes/index.js

module.exports = [
  {
    method: "GET",
    path: "/export",
    handler: "myController.index",
    config: {
      policies: [],
    },
  },
];

(I modified the path field to be "/export" instead of "/" to help remove some ambiguity.)

src/plugins/my-plugin/server/controllers/my-controller.js

"use strict";

module.exports = ({ strapi }) => ({
  index(ctx) {
    ctx.body = strapi
      .plugin("export-form-submissions")
      .service("myService")
      .getWelcomeMessage();
  },
});

now from my admin page (src/plugins/my-plugin/admin/src/pages/HomePage/index.js), I am trying to call this endpoint:

  const handleDownload = async () => {
    try {
      const response = await fetch(`/export`);
      if (response.ok) {
        // File downloaded successfully
      } else {
        // Handle error scenario
      }
    } catch (error) {
    }
  };

However when I call this, I always get a 404 error:
[2023-05-29 17:26:36.715] http: GET /export (2 ms) 404

How do I call this endpoint in my routes files from within my admin page, and as a result call the controller method? Am I missing some sort of configuration to make the endpoint public? I’ve tried lots of variations with my routes and admin page code, but haven’t had any luck…

Many thanks

1 Like

I expect you need to use the URL /export-form-submissions/export in your fetch, as all plugin routes are added under a prefix consisting of the the plugin name.

Thanks for your response. This has solved my issue, but I can’t seem to call the endpoint without adding auth: false to the config. Otherwise I get a 401.

I don’t want to disable the authentication. How do I include authentication to the request?

Instead of raw ‘fetch’, use the helper plugin…

import { getFetchClient } from '@strapi/helper-plugin';
import pluginId from '../../pluginId';

const handleDownload = async () => {
  try {
    const { get } = getFetchClient();
    const response = await get(`/${pluginId}/export`);
    // File downloaded successfully - use response.data
  } catch (error) {
    // Handle error scenario
  }
};

The helper plugin will automate adding the necessary bearer token for the user’s admin session. I don’t know how you would acquire that from the strapi instance object but you could always look at the helper plugin code if you prefer to roll your own ‘fetch’.

2 Likes

Hi,

I have an endpoint called /api/groups created with Content-Type-Builder. Doing a axios request without authorization to this endpoint inside my plugin returns me the data because it is open for public. When doing it like the example you mentioned it returns me a 401 unauthorized and logs me out of the strapi application?

This is my call:

import React, { useState, useEffect } from 'react';
import { getFetchClient } from '@strapi/helper-plugin';
import pluginId from '../../pluginId';

const getGroups = async () => {
    try {
      const { get } = getFetchClient();
      const response = await get('/api/groups');
      console.log(response.data);
    } catch (error) {
      // Handle error scenario
    }
  };
  useEffect(() => {
    getGroups();
  }, []);

Can you or someone help me?

When you use an API endpoint from within a plugin, you are probably using that code from within the Strapi Admin UI - and the user will then be ‘authenticated’. (The point of using the ‘get’ helper is that it adds the bearer authorization automatically.)

When you use an API endpoint from a separate application, you have two options:

  1. Add an API token (generated within the Admin UI) as authorization, or

  2. Change the API endpoint to not require authorization.

For a collection type you created by means of the Content Builder, the code for the respective API endpoint can be found in ~/src/api - one folder per content type. To change the auth type for a particular route, you would need to edit the generated file in the routes folder, and add auth: false to the corresponding route entry. Look in the Strapi docs for details of how to configure a core router - e.g. for a generated API endpoint. The sample code there will show you exactly how to override the default auth requirement - or any other route config parameters.

Also, you only need the ‘get’ helper inside the Admin UI … For a separate front-end app, simply use Axios or indeed just the Fetch API.

1 Like

So what you are basically saying is that I need to use axios because I am doing the request from the Strapi Admin UI and I will be authenticated because of that. I have set the endpoint /api/groups/ only to authenticated and changed my code to this:

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import pluginId from '../../pluginId';

const getGroups = async () => {
  axios.get('http://localhost:1337/api/groups')
    .then(response => {
    console.log(response.data);
  })
    .catch(error => {
    console.error(error);
  });
};
useEffect(() => {
  getGroups();
}, []);

This returns me a 403 Forbidden now. So that won’t work :frowning:

My mistake … I obviously misinterpreted your question.

You say in your first question that an plain axios request returns the data from your endpoint and that the API endpoint was set up to be ‘public’ (not requiring authorisation). From my experience, the default is the opposite - i.e. API endpoints are not ‘public’ - and that a plain axios request will fail, just as your second example does.

Could you please explain how you made the API endpoint public, for your first question?

And could you perhaps try the ‘get’ helper code from your first question, now that the API endpoint requires authorisation. I suspect it may now work.

I have read this thread and I have it working right now! I have added a custom route inside my /plugin/server/routes/index.js and added this handler inside my /plugin/server/controllers/my-controller.js. Inside this handler I make some queries to normally protected strapi endpoints for public.

This new route is protected and with using the getFetchClient() it returns me the expected response :slight_smile:

Thanks for helping!

@jarmo how did you exactly solve it ?
I am having the same issue and same problem:
I am logged-in in the admin ui of Strapi as superadmin.
My plugin is making one api call to /api/data route (which is a normal custom controller under src/api/data)

I get always 401 and logged-out…

  const { get } = getFetchClient();
  const response = await get('/api/data');

my routes/data.ts is:

export default {
    routes: [
        {
            method: 'GET',
            path: '/data',
            handler: 'datat.get',
            config: {
                policies: [],
                middlewares: [],
            },
        },
    ],
};

Do I need to add some middleware or policy or what should I change to get this working?

The right solution for now is the one @jarmo advised above.

@Shekhar

I am using exactly what jamo advised but he is not showing how the route was created and what middleware he used

Can you please let me know how to adjust this route

export default {
    routes: [
        {
            method: 'GET',
            path: '/data',
            handler: 'datat.get',
            config: {
                policies: [],
                middlewares: [],
            },
        },
    ],
};

so that it works when called from a logged-in user in admin panel

Okay. Sharing you a solution which I have used to call the Api from the plugin’s route, controller without using middleware.

Calling API from a Admin component **\src\plugins\plugin-name\admin\src\components\componentName\index.js**)

 const response = await request('/plugin-name/route-name', {
    method: 'POST',
    body: {
      prompt: prompt,
    }
})

Create a route at this path **\src\plugins\plugin-name\server\routes\index.js** and add the below code

module.exports = [
  {
    method: 'POST',
    path: '/route-name',
    handler: 'controllerName.myFunction',
  },
];

Create a controller at this path **\src\plugins\plugin-name\server\controllers\controllerName.js** and add the below code:

'use strict';
module.exports = ({ strapi }) => ({
  async myFunction(ctx) {
    // Your logic (Copy your Normal controller function code here)
  },
});

I hope this can give you a better idea now :slight_smile:

@Shekhar

Thanks a lot for the reply Shekhar.

My issue is that I have done exactly that but I get 401 response code and get unauthorized.

The only difference is that I use getFetchClient in the plugin client side and not request

Maybe the request function is something else. How do you import it in the react side?

I have created my route in Strapi src/api/data and can call it fine with “global api token” and with configuration as posted above.

1 Like

import { request } from ‘@strapi/helper-plugin’;

I tried using request and get same result.
I get 401 response and get logged-out from the admin ui.

I will write again the use case.

I have created one custom Strapi route/api (not inside the plugin) but in Strapi itself

src/api/data

the src/api/data/routes/data.ts

export default {
    routes: [
        {
            method: 'GET',
            path: '/data',
            handler: 'data.get',
            config: {
                policies: [],
                middlewares: [],
            },
        },
    ],
};

src/api/data/controllers/data.ts

export default {
    get: async (ctx, next) => {
        try {
            ctx.body = 'ok';
        } catch (err) {
            ctx.body = err;
        }
    },
};

In the frontend I have created a plugin that makes the calls like

const { get } = getFetchClient();
 const response = await get('/api/data');

I also used request as you suggested

In both cases I get 401 response code and get logged-out from Strapi admin panel

Ahhh I see it after checking the same as yours. The normal Api call when used is throwing back to the login screen.

Is there any specific requirement of yours for which you are calling the Normal src/api/data/routes/data.ts? If not, then give it a try by following the above solution which I gave you (Add your Route & Controller with code) under your plugin folder and check if that works okay for you.

I see what you mean now!
Thanks Shekhar…

I added the controller inside the plugin and now it has endpoint like pluginId/ and it the call is working.

I still find it weird that I cannot use endpoints under /api/ existing strapi endpoints.
Do you know any way how to call these data or other custom endpoints from the admin ui ? and not get 401 response + loggedout

I need in my use case to also fetch some data in my plugin from the /api/data endpoints not only things under /pluginId/…

Welcome!
Not sure why the issue exists in place. Will start digging up and find the issue once I get free time.

In the mean time, anyone from the viewer has the solution for @kristijorgji then share the feedback.

Thanks

1 Like