Handling endpoints APIs on client side instead of serverMiddleware in Nuxt - vue.js

I'm on Nuxt 2.15.8 and trying to build an offline app with electron.js and prisma+sqlite for local DB.
In nuxt to hit a local endpoint there is a common way of using serverMiddleware and express like this:
// api.js that will be added to nuxt.config.js file as serverMiddleware
import express from 'express'
const app = express()
app.use(express.json())
export default {
path: '/api',
handler: app
}
which send endpoints beginning with api/ through app handler which I can use to access my BD (the common way to access sqlite3 DB is the same)
// added to api.js
import { PrismaClient } from '../../resources/prisma/client'
const prisma = new PrismaClient()
app.get(`/user/info`, async (req, res) => {
const result = await prisma.user.findUnique({
where: {
id: 1,
},
})
console.console.log(res);
res.json(result)
})
this will work fine on nuxt, also fine on nuxt-electron dev mode. but on built exe file serverMiddleware won't be called. So as it has be done by others (nuxt-electron accessing offline local DB) there must be a way to define endpoints on client side. any idea??
Updated:
as I changed my Nuxt-Electron boilerplate I could access serverMiddleware in exe file but it wont hit the endpoints yet!

Related

Do we face any issues with creating apollo server and converting it to express server using apollo/server npm package?

pls check the below code,
// npm install #apollo/server express graphql cors body-parser
import { ApolloServer } from '#apollo/server';
import { expressMiddleware } from '#apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '#apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import cors from 'cors';
import bodyParser from 'body-parser';
import { typeDefs, resolvers } from './schema';
// Required logic for integrating with Express
const app = express();
// Our httpServer handles incoming requests to our Express app.
// Below, we tell Apollo Server to "drain" this httpServer,
// enabling our servers to shut down gracefully.
const httpServer = http.createServer(app);
// Same ApolloServer initialization as before, plus the drain plugin
// for our httpServer.
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
// Ensure we wait for our server to start
await server.start();
// Set up our Express middleware to handle CORS, body parsing,
// and our expressMiddleware function.
app.use(
'/',
cors(),
bodyParser.json(),
// expressMiddleware accepts the same arguments:
// an Apollo Server instance and optional configuration options
expressMiddleware(server, {
context: async ({ req }) => ({ token: req.headers.token }),
}),
);
// Modified server startup
await new Promise((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000/`);
The code is from official documentation of apollo server, My question is, here we are first creating an apollo server and converting it to express server using expressMiddleware(server),
Will there be any issues with this conversion ?
Will we face any lack of control over the converted express server ?
My question is, here we are first creating an apollo server and converting it to express server using expressMiddleware(server),
That's not what you're doing at all.
You created an express server (const app = express()).
You created a separate http server from that app.
You created a running executable GraphQL Server.
You created an express middleware ((req, res, next) => void) that generates a context of { token?: string }, parses the body of your request into a GraphQL payload and then executes against the GraphQL Server with that context and with that payload.
You then added that express middleware to a route you created at "/", along with other middleware you attached before it.
You started the httpServer you created in #2
Will there be any issues with this conversion ?
Will we face any lack of control over the converted express server ?
This is no conversion, as mentioned. Hopefully you now understand that.
You created all of those things. Your express app is a variable called app. Do whatever you want with it. Your http server is a variable called httpServer. Do whatever you want with it.

swagger express ui strips the prefix and redirects to host

I am not able to load swagger for my nestJS application which is deployed on EKS.
Here is my main.ts
import {NestFactory} from '#nestjs/core';
import {NestExpressApplication} from '#nestjs/platform-express';
import {DocumentBuilder, SwaggerModule} from '#nestjs/swagger';
const swaggerUi = require('swagger-ui-express')
import {AppModule} from './app.module';
import {json, NextFunction, Request, Response} from 'express';
(async () => {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
const options = new DocumentBuilder()
.setTitle('Test')
.build();
const document = SwaggerModule.createDocument(app, options,{ ignoreGlobalPrefix: true
app.use("*/docs",swaggerUi.serve,swaggerUi.setup(document));
// Or
SwaggerModule.setup(`/docs`, app, document);
await app.listen(port, () => {
console.log(`Test service is running on port ${port}...`);
});
})
();
On my local machine the swagger doc loads correctly but when I deploy it to a development environment, it fails
dev url "https://dev-test-api.com/testservice/v1/docs/", it strips the /testservice/v1/ and redirects to "https://dev-test-api.com/docs/ and gives a 404.
I have tried couple of solutions mentioned in https://github.com/scottie1984/swagger-ui-express/issues/183
The forward-prefix option and the redirect too. None seem to work
Another problem I have is that the prefix "testservice/v1" changes as per different sandbox environments and since it is not an environment variable I don't have a way to set the path for swagger file before app is loaded.

Insomnia auth0 stuck in authentication loop

I'm trying to follow this guide to setup testing an express API in Insomnia. I have setup a poretected route at http://localhost:5000/api/v1/private/dashboard with express-openid-connect. I have also setup an machine-to-machine application and API in auth0.
When in Insomina I try to access http://localhost:5000/api/v1/private/dashboard I am redirected to the universal login page. Trying to login does nothing. I don't think I've set this up right, but am strugging to understand the process and can't find a similar issue when searching.
I want to be able to make GET/POST/PUT/DELETE requests in Insomnia and the data be returned the same as unauthenticated routes.
I have tried various configuration changes including using client credentials, in body, using a different app type but I don't really understand auth0's configuration so don't know what I'm doing.
server.js (where the authenication check is called via oidc)
import express from "express";
import oidc from "express-openid-connect";
import publicRoutes from "./routes/publicRoutes.js";
import privateRoutes from "./routes/privateRotues.js";
const app = express();
const {
auth,
requiresAuth,
} = oidc;
const config = {
authRequired: false,
auth0Logout: true,
secret: process.env.SECRET,
baseURL: process.env.BASE_URL,
clientID: process.env.CLIENT_ID,
issuerBaseURL: process.env.ISSUER_BASE_URL,
};
console.log(config);
app.use(auth(config));
app.use("/api/v1/public", publicRoutes);
app.use("/api/v1/private", requiresAuth(), privateRoutes);
app.use("*", (req, res) => res.status(404).json({error: "File not found"}));
export default app;

How do I mock server-side API calls in a Nextjs app?

I'm trying to figure out how to mock calls to the auth0 authentication backend when testing a next js app with React Testing Library. I'm using auth0/nextjs-auth0 to handle authentication. My intention is to use MSW to provide mocks for all API calls.
I followed this example in the nextjs docs next.js/examples/with-msw to set up mocks for both client and server API calls. All API calls generated by the auth0/nextjs-auth0 package ( /api/auth/login , /api/auth/callback , /api/auth/logout and /api/auth/me) received mock responses.
A mock response for /api/auth/me is shown below
import { rest } from 'msw';
export const handlers = [
// /api/auth/me
rest.get(/.*\/api\/auth\/me$/, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
user: { name: 'test', email: 'email#domain.com' },
}),
);
}),
];
The example setup works fine when I run the app in my browser. But when I run my test the mocks are not getting picked up.
An example test block looks like this
import React from 'react';
import {render , screen } from '#testing-library/react';
import Home from 'pages/index';
import App from 'pages/_app';
describe('Home', () => {
it('should render the loading screen', async () => {
render(<App Component={Home} />);
const loader = screen.getByTestId('loading-screen');
expect(loader).toBeInTheDocument();
});
});
I render the page inside the App component like this <App Component={Home} /> so that I will have access to the various contexts wrapping the pages.
I have spent about 2 days on this trying out various configurations and I still don't know what I might be doing wrong. Any and every help is appreciated.
This is probably resolved already for the author, but since I ran into the same issue and could not find useful documentation, this is how I solved it for end to end tests:
Overriding/configuring the API host.
The plan is to have the test runner start next.js as custom server and then having it respond to both the next.js, as API routes.
A requirements for this to work is to be able to specify the backend (host) the API is calling (via environment variables). Howerver, access to environment variables in Next.js is limited, I made this work using the publicRuntimeConfig setting in next.config.mjs. Within that file you can use runtime environment variables which then bind to the publicRuntimeConfig section of the configuration object.
/** #type {import('next').NextConfig} */
const nextConfig = {
(...)
publicRuntimeConfig: {
API_BASE_URL: process.env.API_BASE_URL,
API_BASE_PATH: process.env.API_BASE_PATH,
},
(...)
};
export default nextConfig;
Everywhere I reference the API, I use the publicRuntimeConfig to obtain these values, which gives me control over what exactly the (backend) is calling.
Allowing to control the hostname of the API at runtime allows me to change it to the local machines host and then intercept, and respond to the call with a fixture.
Configuring Playwright as the test runner.
My e2e test stack is based on Playwright, which has a playwright.config.ts file:
import type { PlaywrightTestConfig } from '#playwright/test';
const config: PlaywrightTestConfig = {
globalSetup: './playwright.setup.js',
testMatch: /.*\.e2e\.ts/,
};
export default config;
This calls another file playwright.setup.js which configures the actual tests and backend API mocks:
import {createServer} from 'http';
import {parse} from 'url';
import next from 'next';
import EndpointFixture from "./fixtures/endpoint.json";
// Config
const dev = process.env.NODE_ENV !== 'production';
const baseUrl = process?.env?.API_BASE_URL || 'localhost:3000';
// Context
const hostname = String(baseUrl.split(/:(?=\d)/)[0]).replace(/.+:\/\//, '');
const port = baseUrl.split(/:(?=\d)/)[1];
const app = next({dev, hostname, port});
const handle = app.getRequestHandler();
// Setup
export default async function playwrightSetup() {
const server = await createServer(async (request, response) => {
// Mock for a specific endpoint, responds with a fixture.
if(request.url.includes(`path/to/api/endpoint/${EndpointFixture[0].slug}`)) {
response.write(JSON.stringify(EndpointFixture[0]));
response.end();
return;
}
// Fallback for pai, notifies about missing mock.
else if(request.url.includes('path/to/api/')) {
console.log('(Backend) mock not implementeded', request.url);
return;
}
// Regular Next.js behaviour.
const parsedUrl = parse(request.url, true);
await handle(request, response, parsedUrl);
});
// Start listening on the configured port.
server.listen(port, (error) => {
console.error(error);
});
// Inject the hostname and port into the applications publicRuntimeConfig.
process.env.API_BASE_URL = `http://${hostname}:${port}`;
await app.prepare();
}
Using this kind of setup, the test runner should start a server which responds to both the routes defined by/in Next.js as well as the routes intentionally mocked (for the backend) allowing you to specify a fixture to respond with.
Final notes
Using the publicRuntimeConfig in combination with a custom Next.js servers allows you to have a relatively large amount of control about the calls that are being made on de backend, however, it does not necessarily intercept calls from the frontend, the existing frontend mocks might stil be necessary.

React Router, pushState with an Express Server

quick question regarding using React-Router. I'm having trouble getting my server to handle pushState (if this is the correct term). Originally, I was using a module called connect-history-api-fallback, which was a middleware that enabled me to only server up static files form my dist directory. Visiting the client www.example.com obviously worked and I could navigate throughout the site, additionally, refreshing at any route like www.example.com/about - could also work.
However, I recently added one simple API endpoint on my Express server for the React app/client to ping. The problem now is that while I can get the initial page load to work (and thus the /api/news call to work, to fetch data from a remote service), I can no longer do a refresh on any other routes. For example, now going to www.example.com/about will result in a failed GET request for /about. How can I remediate this? Really appreciate the help! PS - not sure if it matters, but I'm considering implementing Server Side Rendering later on.
import express from 'express';
import historyApiFallback from 'connect-history-api-fallback';
import config from '../config';
import chalk from 'chalk';
import fetch from 'node-fetch';
import path from 'path';
const app = express();
// FIXME: Unsure whether or not this can be used.
// app.use(historyApiFallback({
// verbose : true
// }));
//// DEVELOPMENT MODE ONLY - USING EXPRESS + HMR ////
/* Enable webpack middleware for hot module reloading */
if (config.get('globals').__DEV__) {
const webpack = require('webpack');
const webpackConfig = require('../build/webpack/development_hot');
const compiler = webpack(webpackConfig);
app.use(require('./middleware/webpack-dev')({
compiler,
publicPath : webpackConfig.output.publicPath
}));
app.use(require('./middleware/webpack-hmr')({ compiler }));
}
//// PRODUCTION MODE ONLY - EXPRESS SERVER /////
if (config.get('globals').__PROD__) {
app.use(express.static(__dirname + '/dist'));
}
//// API ENDPOINTS FOR ALL ENV ////
app.get('/api/news', function (req, res) {
fetch('http://app-service:5000/news')
.then( response => response.json() )
.then( data => res.send(data) )
.catch( () => res.sendStatus(404) );
});
// Wildcard route set up to capture other requests (currently getting undexpected token '<' error in console)
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, '../dist', 'index.html'));
});
export default app;
Express works by implementing a series of middleware that you "plug in" in order via .use. The cool thing is your routes are also just middlware — so you can separate them out, have them before your history fallback, and then only requests that make it past your routes (e.g., didn't match any routes) will hit the fallback.
Try something like the following:
const app = express();
// ...
var routes = exprss.Router();
routes.get('/api/news', function (req, res) {
fetch('http://app-service:5000/news')
.then( response => response.json() )
.then( data => res.send(data) )
.catch( () => res.sendStatus(404) );
});
app.use(routes);
app.use(historyApiFallback({
verbose : true
}));