I am using loopback to build a route:
/users/{id}
where users can listen for new information pertaining to their account on their own endpoint.
I know I need the socket.io package but I'm not sure what to do with it. How can I open a socket on this dynamic endpoint in the function:
#get('/users/{id}', {
responses: {
'200': {
description: 'User socket',
content: {'application/json': {schema: {'x-ts-type': User}}},
},
},
})
async updateUser(#param.path.string('id') userId: typeof User.prototype.id)
: Promise<boolean> {
\\ Open socket here
console.log(userId)
return true;
}
if I do this:
const express = require("express");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io").listen(server);
const port = 3000;
io.on("connection", socket => {
console.log("User has connected!");
});
It doesn't open a socket on the dynamic endpoint that I want it to.
I am using loopback-4 for the backend and react-native for the front-end.
Related
Currently I am trying to deploy my Shopify Custom App to Fly.io. Installing this app is succeeding on my development store but I get an error right after with the oAuth callback with status code 400. This is the URL it shows upon installing:
https://appname.fly.dev/api/auth/callback?code=71bfdaadd63b87eb72d9d3dc516ea1ea&hmac=1efd4ff63ebca8f28c733f464ded354ba2f0995aeb1910114e0139eaefd4cce3&host=YWRtaW4uc2hvcGlmeS5jb20vc3RvcmUvc2hvb3B5bG9vcHkx&shop=shoopyloopy1.myshopify.com&state=920113322594675×tamp=1676563785
With text in body: Invalid OAuth callback.
The app works with all the callbacks working with a ngrok tunnel during development. Just not when deployed to fly.io. The apps frontend also works after deployment to fly.io, but all the api and auth callbacks fail to work. I get the following response on those API calls:
On performing API calls on the /api/ route I get the following error in the return of the api call:
Failed to parse session token 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvc2hvb3B5bG9vcHkxLm15c2hvcGlmeS5jb21cL2FkbWluIiwiZGVzdCI6Imh0dHBzOlwvXC9zaG9vcHlsb29weTEubXlzaG9waWZ5LmNvbSIsImF1ZCI6IjU4YTAzZjkwZTk4Yjc5NGRlZmE5NDZlMWZiNmVlMzRiIiwic3ViIjoiNzQ3NzAxNTM2NjEiLCJleHAiOjE2NzY1NjQyMjYsIm5iZiI6MTY3NjU2NDE2NiwiaWF0IjoxNjc2NTY0MTY2LCJqdGkiOiI0OTcyNDEwOC0zNWQ2LTRjODEtOWJkNS0wZWRkMWM4MWIxMDYiLCJzaWQiOiIxOGZmZjg5NTMyZGRiODdiOWQ3OTBhYmY1M2EwOTZiMDNkNmE4ZWU1ZTA0ZmRjZmFmOWUxOWM2OGQxZGFjN2Q2In0.XeuA5W95YjjVLZYOvmRJ9a90xpPNEukhNQ1_z4Kw_xA': signature verification failed
My fly.toml file:
app = "appname"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []
[env]
PORT = "8081"
HOST = "https://appname.fly.dev"
SHOPIFY_API_KEY = "58a03f90e98b794defa946e1fb6ee34b"
SCOPES = "write_products,read_script_tags,write_script_tags"
[experimental]
auto_rollback = true
[[services]]
http_checks = []
internal_port = 8081
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"
My index.js file starting up express:
// #ts-check
import { join } from "path";
import { readFileSync } from "fs";
import express from "express";
import serveStatic from "serve-static";
import shopify from "./shopify.js";
import productCreator from "./product-creator.js";
import GDPRWebhookHandlers from "./gdpr.js";
import {addScriptTag, deleteScriptTag, getProductInfo, getProductsFromIds, getScriptTags} from "./graph-functions.js";
const PORT = parseInt(process.env.BACKEND_PORT || process.env.PORT, 10);
const STATIC_PATH =
process.env.NODE_ENV === "production"
? `${process.cwd()}/frontend/dist`
: `${process.cwd()}/frontend/`;
const app = express();
// Set up Shopify authentication and webhook handling
app.get(shopify.config.auth.path, shopify.auth.begin());
app.get(
shopify.config.auth.callbackPath,
shopify.auth.callback(),
shopify.redirectToShopifyOrAppRoot()
);
app.post(
shopify.config.webhooks.path,
shopify.processWebhooks({ webhookHandlers: GDPRWebhookHandlers })
);
// All endpoints after this point will require an active session
app.use("/api/*", shopify.validateAuthenticatedSession());
app.use(express.json());
app.get("/api/products/count", async (_req, res) => {
const countData = await shopify.api.rest.Product.count({
session: res.locals.shopify.session,
});
res.status(200).send(countData);
});
app.get("/api/get-product", async (_req, res) => {
const products = await getProductInfo(res.locals.shopify.session, _req.query.id);
res.status(200).send(products);
});
app.get("/api/get-products", async (_req, res) => {
const products = await getProductsFromIds(res.locals.shopify.session,_req.query.ids);
res.status(200).send(products);
});
app.get("/api/add-script", async (_req, res) => {
const data = await addScriptTag(res.locals.shopify.session,_req.query.src);
console.log(_req.query.ids);
res.status(200).send(data);
});
app.get("/api/get-scripts", async (_req, res) => {
const data = await getScriptTags(res.locals.shopify.session);
res.status(200).send(data?.body?.data ? data?.body?.data : data);
});
app.get("/api/delete-script", async (_req, res) => {
const data = await deleteScriptTag(res.locals.shopify.session,_req.query.id);
res.status(200).send(data);
});
app.get("/api/products/create", async (_req, res) => {
let status = 200;
let error = null;
try {
await productCreator(res.locals.shopify.session);
} catch (e) {
console.log(`Failed to process products/create: ${e.message}`);
status = 500;
error = e.message;
}
res.status(status).send({ success: status === 200, error });
});
app.use(serveStatic(STATIC_PATH, { index: false }));
app.use("/*", shopify.ensureInstalledOnShop(), async (_req, res, _next) => {
return res
.status(200)
.set("Content-Type", "text/html")
.send(readFileSync(join(STATIC_PATH, "index.html")));
});
app.listen(PORT);
Any help would be highly appreciated.
I followed the official documentation: Shopify Official Docs
The Dockerfile has the same port 8081 as assigned to in the fly.toml file.
Edit (Added shopify app implementation with Database):
import { LATEST_API_VERSION } from "#shopify/shopify-api";
import { shopifyApp } from "#shopify/shopify-app-express";
import { SQLiteSessionStorage } from "#shopify/shopify-app-session-storage-sqlite";
import { restResources } from "#shopify/shopify-api/rest/admin/2023-01";
const DB_PATH = `${process.cwd()}/database.sqlite`;
const shopify = shopifyApp({
api: {
apiVersion: LATEST_API_VERSION,
restResources,
billing: undefined, // or replace with billingConfig above to enable example billing
},
auth: {
path: "/api/auth",
callbackPath: "/api/auth/callback",
},
webhooks: {
path: "/api/webhooks",
},
// This should be replaced with your preferred storage strategy
sessionStorage: new SQLiteSessionStorage(DB_PATH),
});
export default shopify;
I am using an Express JS to initiate websocket connection. Below is the following configuration that I used.
socket.js
const ip_address = `${process.env.MIX_HTTPS_APP_URL}`;
const socket_port = `${process.env.MIX_EXPRESS_PORT}`;
const URL = ip_address + ":" + socket_port;
export const socket = io(URL, { autoConnect: true });
socket.onAny((event, ...args) => {
// console.log(event, args);
});
index.js
const app = express();
// app settings
/** Create HTTP server. */
const privateKey = fs.readFileSync(env.privateKey, "utf8");
const certificate = fs.readFileSync(env.certificate, "utf8");
const options = {
key: privateKey,
cert: certificate,
};
const server = https.createServer(options, app).listen(port);
/** Create socket connection */
const socketio = new Server(server);
global.io = socketio.listen(server, {
cors: {
origin: env.url,
},
});
global.io.on("connection", WebSockets.connection);
/** Listen on provided port, on all network interfaces. */
/** Event listener for HTTP server "listening" event. */
server.on("listening", () => {
console.log(`Listening on port:: ${env.url}:${port}/`);
});
Though I can emit and listen to socket, I am still getting this error message. How should I solve this so that users wont see this error message when they view the console?
With reference to your example, the listening on port should be on http server, & socket.io should just attach to on new conn.
see doc https://socket.io/docs/v4/server-initialization/#with-an-https-server
Hope this helps.
I have a Nest-Service with the following main.ts:
async function bootstrap() {
if (!!environment.production) {
const app = await NestFactory.create(AppModule, {
httpsOptions: {
key: fs.readFileSync(environment.ssl.SSL_KEY_PATH),
cert: fs.readFileSync(environment.ssl.SSL_CERT_PATH)
},
});
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.listen(3077);
} else {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.listen(3077);
}
}
bootstrap();
And two Gateways within the Service:
#WebSocketGateway(3078)
export class ItemsGateway implements OnGatewayConnection, OnGatewayDisconnect { ... }
#WebSocketGateway(3079)
export class UnitsGateway implements OnGatewayConnection, OnGatewayDisconnect { ... }
Without SSL this is working, but when I use the prod mode I canΒ΄t establish a secure connection to domain.tld:3078 and :3079.
How can I get the service to listen on all 3 Ports? I think there is the problem, because certs are only attached to the Server listening on Port: 3077, where all my REST-API stuff goes.
Thx, Dom
Edit: This also worked as there was just on WebsocketServer on the same port as the API -> 3077.
Edit 2:
I also tried this, but then comes the error that address is in use on the second attempt to create() a server:
async function bootstrap() {
if (!!environment.production) {
const httpsOptions = {
key: fs.readFileSync(environment.ssl.SSL_KEY_PATH),
cert: fs.readFileSync(environment.ssl.SSL_CERT_PATH)
};
const server = express();
const app = await NestFactory.create(
AppModule,
new ExpressAdapter(server)
);
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.init();
https.createServer(httpsOptions, server).listen(environment.app.port);
https.createServer(httpsOptions, server).listen(environment.websocketPorts.units);
https.createServer(httpsOptions, server).listen(environment.websocketPorts.items);
} else {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));
app.enableCors();
await app.listen(environment.app.port);
}
}
bootstrap();
You need to .create() a separate app for each port on which you listen for wss connections.
I've managed to have a express + Apollo Backend as a serverMiddleware in Nuxtjs.
Everything works fine(auth, cache, datasources, queries, mutations) but now I'm trying to get subscriptions(websockets) running and its giving me a hard time.
I tried this example https://www.apollographql.com/docs/apollo-server/data/subscriptions/#subscriptions-with-additional-middleware but even letting the httpServer listening didn't work.
This is my API file which I require through the nuxt.config.js with '~/api/index' :
module.exports = async () => {
const app = require('express')()
const server = await require("./apollo")() // apollo-server-express w/ typeDefs and resolvers
// apply Apollo to Express
server.applyMiddleware({ app });
console.log(`π ApolloServer ready at ${server.graphqlPath}`);
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
console.log(`π ApolloSubscriptions ready at ${server.subscriptionsPath}`);
return {
path: '/api',
handler: httpServer
}
}
Now my playground is giving me this error: "Could not connect to websocket endpoint ws://192.168.150.98:3000/api/graphql. Please check if the endpoint url is correct."
TypeDefs:
type Subscription {
postAdded: Post
}
type Post {
author: String
comment: String
}
type Query {
posts: [Post]
}
type Mutation {
addPost(author: String, comment: String): Post
}
Resolvers:
Query: {
posts(root, args, context) {
return Posts;
}
}
Mutation: {
addPost(root, args, context) {
pubsub.publish(POST_ADDED, { postAdded: args });
return Posts.add(args);
}
},
Subscription: {
postAdded: {
// Additional event labels can be passed to asyncIterator creation
subscribe: () => pubsub.asyncIterator([POST_ADDED]),
},
}
First question here, thank u in advance! :)
it can also be a little easier
1.
yarn add apollo-server-express
or
npm install apollo-server-express
create file ./server/index.js
import { ApolloServer, gql } from 'apollo-server-express'
// Construct a schema, using GraphQL schema language
const typeDefs = gql`
type Query {
hello: String
}
`
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
}
const server = new ApolloServer({ typeDefs, resolvers })
export default server
add in your nuxt.config.js
import server from './server'
export default {
// ... your nuxt config stuff
// ...
hooks: {
render: {
async before({
nuxt: {
server: { app },
},
}) {
await server.applyMiddleware({ app, path: '/api' })
console.log(`π ApolloServer ready at /api`)
},
},
}
}
I found a hacky way to achieve it, import the code as a nuxt module:
import http from 'http'
export default function () {
this.nuxt.hook('render:before', async () => {
const server = require("./apollo")()
// apply Apollo to Express
server.applyMiddleware({ app: this.nuxt.renderer.app });
console.log(`π ApolloServer ready at ${server.graphqlPath}`);
const httpServer = http.createServer(this.nuxt.renderer.app);
// apply SubscriptionHandlers to httpServer
server.installSubscriptionHandlers(httpServer);
console.log(`π ApolloSubscriptions ready at ${server.subscriptionsPath}`);
// overwrite nuxt.server.listen()
this.nuxt.server.listen = (port, host) => new Promise(resolve => httpServer.listen(port || 3000, host || 'localhost', resolve))
// close this httpServer on 'close' event
this.nuxt.hook('close', () => new Promise(httpServer.close))
})
}
Tho I'm now using a probably more stable way, using nuxt programmatically!
With hapi instead of express, since express is giving me trouble compiling and not showing the loading-screen(progress of building).
Just use npx create-nuxt-app and create an app with a hapi server backend.
The code with hapi would look like this:
const consola = require('consola')
const Hapi = require('#hapi/hapi')
const HapiNuxt = require('#nuxtjs/hapi')
async function start () {
const server = require('./apollo/index')()
const app = new Hapi.Server({
host: process.env.HOST || '127.0.0.1',
port: process.env.PORT || 3000
})
await app.register({
plugin: HapiNuxt
})
app.route(await require('./routes')())
await server.applyMiddleware({
app,
path: '/graphql'
});
console.log(`π ApolloServer ready at ${server.graphqlPath}`);
await server.installSubscriptionHandlers(app.listener)
console.log(`π ApolloSubscriptions ready at ${server.subscriptionsPath}`);
await app.start()
consola.ready({
message: `Server running at: ${app.info.uri}`,
badge: true
})
}
process.on('unhandledRejection', error => consola.error(error))
start().catch(error => console.log(error))
Maybe i can help somebody
An easier way is to use the getMiddleware() method of Apollo Server Express:
Create a file under ./api/index.js:
const { ApolloServer, gql } = require('apollo-server-express')
const express = require('express')
const typeDefs = gql`
type Query {
hello: String
}
`
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
}
const server = new ApolloServer({ typeDefs, resolvers })
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(server.getMiddleware())
module.exports = app
and then register it in ./nuxt.config.js:
{
// other nuxt config ...
serverMiddleware: [{ path: '/api', handler: '~/api/index.js' }],
}
I'm trying to connect socket in react native. I am able to connect same socket in html file.
Server Code :
const express = require("express")
, app = express()
, http = require("http").createServer(app)
, fs = require("fs")
, io = require("socket.io")(http);
app.use(express.static("public"));
http.listen("8080", () => {
console.log("Server Started");
});
function socketIdsInRoom(name) {
var socketIds = io.nsps['/'].adapter.rooms[name];
if (socketIds) {
var collection = [];
for (var key in socketIds) {
collection.push(key);
}
return collection;
} else {
return [];
}
}
io.on('connection', function (socket) {
console.log('connection');
socket.on('disconnect', function () {
console.log('disconnect');
});
});
React Native Code:
import io from 'socket.io-client';
const socket = io.connect('http://127.0.0.1:8080', { transports: ['websocket'] });
//const socket = io("http://127.0.0.1:8080"); /* I've also tried this */
socket.on('connect', function (data) {
console.log('connect');
});
I'm using socket.io-client for socket connections in react native
Problem is related to 127.0.0.1 ip. Android can't connect to 127.0.0.1 ip because it is internal loop back IP.
You should replace 127.0.0.1 with your server Ip ether external or internal IP.