-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New handlers: refactored, Web API and AWS Lambda support #380
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: 534b64f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be easier for me to review if I already see docs with examples. In case of SDK public API is more important than the code
@@ -0,0 +1,5 @@ | |||
--- | |||
"@saleor/app-sdk": minor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please make it major since it has breaking changes
"@saleor/app-sdk": minor | ||
--- | ||
|
||
Added `handlers/fetch-api` which adds support for frameworks that use [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remember to add full changeset and docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"./handlers/next-app-router": { | ||
"types": "./handlers/fetch-api/index.d.ts", | ||
"import": "./handlers/fetch-api/index.mjs", | ||
"require": "./handlers/fetch-api/index.js" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
won't it have the same names as fetch api? It would be confusing, probably better to also add alias on export
so next-app-router
file can include export { FetchApi as NextAppRouterWebhook } from '../fetch-api'
or something like this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be easier if we see docs at this point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine, since exported names do not reference Fetch API in any way, names are the same as in Next.js handlers except they are in a different folder. Our exports are:
createAppRegisterHandler
createManifestHandler
createProtectedHandler
SaleorSyncWebhook
SaleorAsyncWebhook
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another partial review ;)
src/fetch-middleware/index.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is "fetch middleware"? If this is a middleware using Fetch Request API, I don't think we should call it that way. Maybe just "middleware"?
@@ -0,0 +1,5 @@ | |||
export type SaleorRequest = Request & { context?: Record<string, any> }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably you should avoid any
in favor of unknown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add tests to all middlewares
|
||
const debug = createFetchMiddlewareDebug("withRegisteredSaleorDomainHeader"); | ||
|
||
export const withRegisteredSaleorDomainHeader: FetchMiddleware = (handler) => async (request) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are actually checking "saleorApiUrl", not domain, and it 1.0.0 we will drop domain checking
); | ||
} | ||
|
||
const authData = await saleorApp?.apl.get(saleorApiUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe can we extract this? Middleware checks header but also checks if auth data exists. And doesnt do anything with it. It doesnt event attach it to request, so it's cached
|
||
const debug = createFetchMiddlewareDebug("withSaleorDomainPresent"); | ||
|
||
export const withSaleorDomainPresent: FetchMiddleware = (handler) => async (request) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can remove this, in v1 we can remove domain header, so no need to check this
const checksToRun = [ | ||
this.adapterMiddleware.withMethod(["POST"]), | ||
this.adapterMiddleware.withSaleorDomainPresent(), | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not sure if middleware should work as "check" only. If you check against e.g. existence of AuthData, you can pass Request and middleware can enrich it with context - eg attach AuthData there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think modifying Request
object is a good idea. It would be like adding new properties to globals like Array
or Window
😬 These objects are meant to be immutable.
Adding context is usually done by web frameworks that use Fetch API by creating new Context object and passing it everywhere, or by using AsyncLocalStorage. Example in Hono.
We could do this, but then we would need to have some context mechanism of our own. We could also use AsyncLocalStorage, but this is also problematic since runtimes like Cloudflare Workers, or Deno require enabling Node compatibility layer to do this :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modifying request isn't anything new. It's not about modifying global, but instance. It's not about Array.prototype.foo
by (new Array()).foo
Context you mentioned is a platform context.
What I mean is request context. It can be achieved several ways, eg with async_hooks, modifying request or adding another argument (like createWebhookHandler(req, res, ctx)
). Or how TRPC middleware works.
You can also read eg https://expressjs.com/en/guide/using-middleware.html
What I want to achieve is to allow middleware to attach data for next middlewares or handlers and compose the rich request in the end.
For example, if your handler fetches AuthData, it can attach it to request (pass to next step), instead of fetching it again in next middleware/handler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I think we are talking about different things here and maybe the name middleware in this place is wrong and confusing 😅
These "middlewares" do not have any data we would want to pass as context, since they don't do any asynchronous actions.
This specific register action has a concept of "context" in its config, where you can attach to different events, e.g.:
/**
* Run right after Saleor calls this endpoint
*/
onRequestStart?(
request: RequestType,
context: {
authToken?: string;
saleorDomain?: string;
saleorApiUrl?: string;
respondWithError: CallbackErrorHandler;
}
): Promise<void>;
Config shape is the same as previous Next.js actions in app-sdk, it just has different Request
object type depending on platform
createProtectedHandler
also has its own context:
export type ProtectedHandlerContext = {
baseUrl: string;
authData: AuthData;
user: TokenUserPayload;
};
Webhook handlers also have context (no change from previous public API):
export type SaleorWebhookHandler<TPayload = unknown, TExtras = {}> = (
req: Request,
ctx: WebhookContext<TPayload> & TExtras
) => Response | Promise<Response>;
As for fetch-middleware
, please see my comment: #380 (comment).
// TODO: We should strictly require `saleorApiUrl` instead of | ||
// relying on `saleor-domain` that is deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment is stale
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? the comment is still valid because we are not doing strict check on saleorApiUrl
for compatibility reasons (I didn't change bussiness logic, rather I just copied our old code and adapted it to work with our new adapter abstraction).
We can make another breaking change and just drop the Saleor-Domain
usage altogether imho
@lkostrowski Public API usage doesn't change, the change made to existing Next.js handlers are meant to be drop-in and non-breaking. New handlers for Web API and Lambda have the same signature as Next.js handlers, they only differ when accepting a The only breaking change is removing one parameter, which I think was a mistake in our API design, since we were passing I've prepared examples here:
And docs here: saleor/saleor-docs#1437 |
@lkostrowski one more thing: I'm also not sure if we even want to add these middlewares to begin with? They are not used in other places in app-sdk. Previously we had them because they were passed to retes. I also don't think these are used in any of our apps, since most of them use higher level abstractions by using |
Released snapshot build with Install it with: pnpm add @saleor/[email protected] |
This PR will be split into multiple smaller ones for easier review |
Description
This PR introduces new handlers for Fetch Web API and AWS Lambda and refactors existing code so that it can easily allow adding new platforms in the future.
After the change handlers code is splits them into 3 parts:
src/handlers/nextjs
- For handlers that use Next.js platform adaptersrc/handlers/fetch-api
- For handlers that use Fetch API platform adapterThis way we'll be able to easily add new platforms and handlers without changing existing code.
Currently in this PR:
Examples and docs
Usage examples:
https://github.com/witoszekdev/saleor-app-hono-aws-lambda-template
https://github.com/witoszekdev/saleor-app-hono-deno-template
https://github.com/witoszekdev/saleor-app-hono-cf-pages-template
saleor/saleor-app-template#267
Docs:
saleor/saleor-docs#1437
Breaking changes?
SaleorWebhook
acceptsonError
andformatErrorResponse
parameters callbacks, now they won't passres
object in Next.js runtime, since these methods weren't supposed to send responses