Skip to content

Commit

Permalink
Add apiURL and apiGraphQLServerPath to redwood.toml. (redwoodjs#2822
Browse files Browse the repository at this point in the history
)

Co-authored-by: David Price <[email protected]>
Co-authored-by: Daniel Choudhury <[email protected]>
Co-authored-by: David Thyresson <[email protected]>
  • Loading branch information
4 people authored Oct 21, 2021
1 parent b959f59 commit 558a9ce
Show file tree
Hide file tree
Showing 40 changed files with 269 additions and 127 deletions.
37 changes: 25 additions & 12 deletions packages/api-server/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import type { HttpServerParams } from './server'
*/

export const commonOptions = {
port: { default: 8910, type: 'number', alias: 'p' },
port: { default: getConfig().web?.port || 8910, type: 'number', alias: 'p' },
socket: { type: 'string' },
} as const

export const apiCliOptions = {
port: { default: 8911, type: 'number', alias: 'p' },
port: { default: getConfig().api?.port || 8911, type: 'number', alias: 'p' },
socket: { type: 'string' },
apiRootPath: {
alias: ['rootPath', 'root-path'],
Expand All @@ -32,12 +32,12 @@ export const apiCliOptions = {
} as const

export const webCliOptions = {
port: { default: 8910, type: 'number', alias: 'p' },
port: { default: getConfig().web?.port || 8910, type: 'number', alias: 'p' },
socket: { type: 'string' },
apiHost: {
alias: 'api-host',
type: 'string',
desc: 'Forward requests from the apiProxyPath, defined in redwood.toml to this host',
desc: 'Forward requests from the apiUrl, defined in redwood.toml to this host',
},
} as const

Expand All @@ -51,7 +51,7 @@ export const apiServerHandler = async ({
apiRootPath,
}: ApiServerArgs) => {
const tsApiServer = Date.now()
process.stdout.write(c.dim(c.italic('Starting API Server... ')))
process.stdout.write(c.dim(c.italic('Starting API Server...')))
const app = createApp()
const http = startServer({
port,
Expand All @@ -77,7 +77,8 @@ export const bothServerHandler = async ({
port,
socket,
}: Omit<HttpServerParams, 'app'>) => {
const apiRootPath = coerceRootPath(getConfig().web.apiProxyPath)
const apiRootPath = coerceRootPath(getConfig().web.apiUrl)

let app = createApp()

// Attach middleware
Expand All @@ -93,8 +94,10 @@ export const bothServerHandler = async ({
console.log(`Listening on ${socket}`)
}

console.log(`Web server started on http://localhost:${port} `)
console.log(`APIs Listening on http://localhost:${port}${apiRootPath}`)
console.log(`Web server started on ${port} `)
console.log(
`API serving from ${apiRootPath} listening on ${port} with GraphQL endpoint at ${apiRootPath}graphql`
)
})
}

Expand All @@ -103,27 +106,37 @@ interface WebServerArgs extends Omit<HttpServerParams, 'app'> {
}

export const webServerHandler = ({ port, socket, apiHost }: WebServerArgs) => {
const apiUrl = getConfig().web.apiUrl
// Construct the graphql url from apiUrl by default
// But if apiGraphQLUrl is specified, use that instead
const graphqlEndpoint = coerceRootPath(
getConfig().web.apiGraphQLUrl ?? `${getConfig().web.apiUrl}graphql`
)

let app = createApp()

// Attach middleware
// We need to proxy api requests to prevent CORS issues
if (apiHost) {
const apiProxyPath = getConfig().web.apiProxyPath
app = withApiProxy(app, { apiHost, apiProxyPath })
app = withApiProxy(app, {
apiHost,
apiUrl,
})
}

app = withWebServer(app)

startServer({
port,
port: port,
socket,
app,
}).on('listening', () => {
if (socket) {
console.log(`Listening on ${socket}`)
}

console.log(`Web server started on http://localhost:${port} `)
console.log(`Web server started on port ${port} `)
console.log(`GraphQL endpoint is ${apiUrl}${graphqlEndpoint}`)
})
}

Expand Down
8 changes: 4 additions & 4 deletions packages/api-server/src/middleware/withApiProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import type { Application } from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'

interface ApiProxyOptions {
apiProxyPath: string
apiUrl: string
apiHost?: string
}

const withApiProxy = (
app: Application,
{ apiProxyPath, apiHost }: ApiProxyOptions
{ apiUrl, apiHost }: ApiProxyOptions
) => {
// If apiHost is supplied, it means the functions are running elsewhere
// So we should just proxy requests
if (apiHost) {
app.use(
createProxyMiddleware(apiProxyPath, {
createProxyMiddleware(apiUrl, {
changeOrigin: true,
pathRewrite: {
[`^${apiProxyPath}`]: '/', // remove base path
[`^${apiUrl}`]: '/', // remove base path
},
target: apiHost,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"build:js": "babel src -d dist --extensions \".js,.ts,.tsx\"",
"build:types": "tsc --build --verbose",
"build:watch": "nodemon --watch src --ext \"js,ts,tsx\" --ignore dist --exec \"yarn build\"",
"test": "jest",
"test": "jest src",
"test:watch": "yarn test --watch"
},
"gitHead": "8be6a35c2dfd5aaeb12d55be4f0c77eefceb7762"
Expand Down
16 changes: 14 additions & 2 deletions packages/auth/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,24 @@ import type { AuthContextInterface } from './src/index'
declare global {
// For some reason, in this package, we need to declare it here too
var __REDWOOD__USE_AUTH: () => AuthContextInterface
var __REDWOOD__API_PROXY_PATH: string
/**
* FQDN or absolute path to the GraphQL serverless function, without the trailing slash.
* Example: `./redwood/functions/graphql` or `https://api.redwoodjs.com/graphql`
*/
var RWJS_API_GRAPHQL_URL: string
/**
* FQDN or absolute path to the DbAuth serverless function, without the trailing slash.
* Example: `./redwood/functions/auth` or `https://api.redwoodjs.com/auth`
**/
var RWJS_API_DBAUTH_URL: string

namespace NodeJS {
interface Global {
__REDWOOD__USE_AUTH: () => AuthContextInterface
__REDWOOD__API_PROXY_PATH: string
/** FQDN or absolute path to the GraphQL serverless function */
RWJS_API_GRAPHQL_URL: string
/** FQDN or absolute path to the DbAuth serverless function */
RWJS_API_DBAUTH_URL: string
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"build:js": "babel src -d dist --extensions \".js,.ts,.tsx\"",
"build:types": "tsc --build --verbose",
"build:watch": "nodemon --watch src --ext \"js,ts,tsx\" --ignore dist --exec \"yarn build\"",
"test": "jest",
"test": "jest src",
"test:watch": "yarn test --watch"
},
"gitHead": "8be6a35c2dfd5aaeb12d55be4f0c77eefceb7762"
Expand Down
31 changes: 16 additions & 15 deletions packages/auth/src/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,25 @@ export class AuthProvider extends React.Component<
return this.reauthenticate()
}

getApiGraphQLUrl = () => {
return global.RWJS_API_GRAPHQL_URL
}

getCurrentUser = async (): Promise<Record<string, unknown>> => {
// Always get a fresh token, rather than use the one in state
const token = await this.getToken()
const response = await global.fetch(
`${global.__REDWOOD__API_PROXY_PATH}/graphql`,
{
method: 'POST',
headers: {
'content-type': 'application/json',
'auth-provider': this.rwClient.type,
authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query:
'query __REDWOOD__AUTH_GET_CURRENT_USER { redwood { currentUser } }',
}),
}
)
const response = await global.fetch(this.getApiGraphQLUrl(), {
method: 'POST',
headers: {
'content-type': 'application/json',
'auth-provider': this.rwClient.type,
authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query:
'query __REDWOOD__AUTH_GET_CURRENT_USER { redwood { currentUser } }',
}),
})

if (response.ok) {
const { data } = await response.json()
Expand Down
3 changes: 2 additions & 1 deletion packages/auth/src/__tests__/AuthProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ let CURRENT_USER_DATA: { name: string; email: string; roles?: string[] } = {
email: '[email protected]',
}

global.__REDWOOD__API_PROXY_PATH = '/.netlify/functions'
global.RWJS_API_GRAPHQL_URL = '/.netlify/functions/graphql'

const server = setupServer(
graphql.query('__REDWOOD__AUTH_GET_CURRENT_USER', (_req, res, ctx) => {
return res(
Expand Down
14 changes: 7 additions & 7 deletions packages/auth/src/authClients/dbAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type DbAuth = () => null

export const dbAuth = (): AuthClient => {
const forgotPassword = async (username: string) => {
const response = await fetch(`${global.__REDWOOD__API_PROXY_PATH}/auth`, {
const response = await fetch(global.RWJS_API_DBAUTH_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, method: 'forgotPassword' }),
Expand All @@ -26,7 +26,7 @@ export const dbAuth = (): AuthClient => {

const getToken = async () => {
const response = await fetch(
`${global.__REDWOOD__API_PROXY_PATH}/auth?method=getToken`
`${global.RWJS_API_DBAUTH_URL}?method=getToken`
)
const token = await response.text()

Expand All @@ -39,7 +39,7 @@ export const dbAuth = (): AuthClient => {

const login = async (attributes: LoginAttributes) => {
const { username, password } = attributes
const response = await fetch(`${global.__REDWOOD__API_PROXY_PATH}/auth`, {
const response = await fetch(global.RWJS_API_DBAUTH_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password, method: 'login' }),
Expand All @@ -48,15 +48,15 @@ export const dbAuth = (): AuthClient => {
}

const logout = async () => {
await fetch(`${global.__REDWOOD__API_PROXY_PATH}/auth`, {
await fetch(global.RWJS_API_DBAUTH_URL, {
method: 'POST',
body: JSON.stringify({ method: 'logout' }),
})
return true
}

const resetPassword = async (attributes: ResetPasswordAttributes) => {
const response = await fetch(`${global.__REDWOOD__API_PROXY_PATH}/auth`, {
const response = await fetch(global.RWJS_API_DBAUTH_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...attributes, method: 'resetPassword' }),
Expand All @@ -65,7 +65,7 @@ export const dbAuth = (): AuthClient => {
}

const signup = async (attributes: SignupAttributes) => {
const response = await fetch(`${global.__REDWOOD__API_PROXY_PATH}/auth`, {
const response = await fetch(global.RWJS_API_DBAUTH_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...attributes, method: 'signup' }),
Expand All @@ -74,7 +74,7 @@ export const dbAuth = (): AuthClient => {
}

const validateResetToken = async (resetToken: string | null) => {
const response = await fetch(`${global.__REDWOOD__API_PROXY_PATH}/auth`, {
const response = await fetch(global.RWJS_API_DBAUTH_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ resetToken, method: 'validateResetToken' }),
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"build:js": "babel src -d dist --extensions \".js,.ts,.tsx\" --copy-files --no-copy-ignored && yarn build:clean-dist",
"fix:permissions": "chmod +x dist/index.js dist/redwood-tools.js dist/rwfw.js",
"build:watch": "nodemon --watch src --ext \"js,ts,tsx,template\" --ignore dist --exec \"yarn build && yarn fix:permissions\"",
"test": "jest",
"test": "jest src",
"test:watch": "yarn test --watch"
},
"gitHead": "8be6a35c2dfd5aaeb12d55be4f0c77eefceb7762"
Expand Down
19 changes: 8 additions & 11 deletions packages/cli/src/commands/setup/deploy/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,19 @@ const SUPPORTED_PROVIDERS = fs
.map((file) => path.basename(file, '.js'))
.filter((file) => file !== 'README.md')

const updateProxyPath = (newProxyPath) => {
const updateApiURL = (apiUrl) => {
const redwoodToml = fs.readFileSync(REDWOOD_TOML_PATH).toString()
let newRedwoodToml = redwoodToml

if (redwoodToml.match(/apiProxyPath/)) {
newRedwoodToml = newRedwoodToml.replace(
/apiProxyPath.*/g,
`apiProxyPath = "${newProxyPath}"`
)
if (redwoodToml.match(/apiUrl/)) {
newRedwoodToml = newRedwoodToml.replace(/apiUrl.*/g, `apiUrl = "${apiUrl}"`)
} else if (redwoodToml.match(/\[web\]/)) {
newRedwoodToml = newRedwoodToml.replace(
/\[web\]/,
`[web]\n apiProxyPath = "${newProxyPath}"`
`[web]\n apiUrl = "${apiUrl}"`
)
} else {
newRedwoodToml += `[web]\n apiProxyPath = "${newProxyPath}"`
newRedwoodToml += `[web]\n apiUrl = "${apiUrl}"`
}

fs.writeFileSync(REDWOOD_TOML_PATH, newRedwoodToml)
Expand Down Expand Up @@ -150,10 +147,10 @@ export const handler = async ({ provider, force, database }) => {
await execa('yarn', ['install'])
},
},
providerData?.apiProxyPath && {
title: 'Updating apiProxyPath...',
providerData?.apiUrl && {
title: 'Updating API URL...',
task: async () => {
updateProxyPath(providerData.apiProxyPath)
updateApiURL(providerData.apiUrl)
},
},
providerData?.files?.length && {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,17 @@ ${
memorySize: 1024 # mb
timeout: 25 # seconds (max: 29)
tags: # Tags for this specific lambda function
endpoint: ${config.web.apiProxyPath}/${basename}
endpoint: ${config.web.apiUrl}/${basename}
# Uncomment this section to add environment variables either from the Serverless dotenv plugin or using Serverless params
# environment:
# YOUR_FIRST_ENV_VARIABLE: \${env:YOUR_FIRST_ENV_VARIABLE}
handler: ${basename}.handler
events:
- httpApi:
path: ${config.web.apiProxyPath}/${basename}
path: ${config.web.apiUrl}/${basename}
method: GET
- httpApi:
path: ${config.web.apiProxyPath}/${basename}
path: ${config.web.apiUrl}/${basename}
method: POST
`
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const files = [
{ path: path.join(getPaths().base, 'netlify.toml'), content: NETLIFY_TOML },
]

export const apiProxyPath = '/.netlify/functions'
export const apiUrl = '/.netlify/functions'

// any notes to print out when the job is done
export const notes = [
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/setup/deploy/providers/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export const files = [
},
]

export const apiProxyPath = '/.redwood/functions'
export const apiUrl = '/.redwood/functions'

// any notes to print out when the job is done
export const notes = [
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/setup/deploy/providers/vercel.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const apiProxyPath = '/api'
export const apiUrl = '/api'

// any notes to print out when the job is done
export const notes = [
Expand Down
Loading

0 comments on commit 558a9ce

Please sign in to comment.