-
Notifications
You must be signed in to change notification settings - Fork 44
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
Improve Node.js Functions programming model 🚀 #480
Comments
One of the hardest things to handle is bindings - there's nothing like .NET attributes that you can leverage, so there's really two options IMO - object literal or a fluent API. Here's some rough prototype ideas: import app from "@azure/functions";
// object literal
app.register(async (context: FunctionContext) => {
context.res = {
body: { hello: "world" }
};
}, {(
bindings: [{
type: "http",
direction: "in",
name: "req",
methods: ["get", "post"]
}, {
type: "http",
direction: "out",
name: "res"
}],
route: "/echo"
});
// fluent API
app.register(async (context: FunctionContext) => {
context.res = {
body: { hello: "world" }
}
}).binding({
type: "http",
direction: "in",
name: "req",
methods: ["get", "post"]
}).binding({
type: "http",
direction: "out",
name: "res"
}).route("echo"); Both of those are a "low level API" and I don't see why you couldn't have something more like I've also only done HTTP here, but there's nothing HTTP-centric around this model. |
I'd be looking at libraries like Fastify/Express/etc. as inspirations for how to create the API surface area, but not try and mimic them completely as they are centred so much around HTTP only, when you want to make a programming model that fits nicely for other bindings too. |
I feel honored to be in the bullet list of items that triggers this issue. JK. JK. :P |
With the prominence of TypeScript, I would suggest thinking of how the new API model could leverage TS to make it a first class citizen. For example, I would push forward TS decorators as a way to define bindings and other functions settings. I know decorators' TC39 proposal is currently only stage 2, and TS decorators are already diverging a bit from that proposal. But as syntactic sugar, they're unmatched and AFAIK all most popular TS-first frameworks tools use decorators: Angular, NestJS, TypeORM, TypeGraphQL, Loopback, Ts.ED... This is not necessarily incompatible with @ejizba's prototype approach, but could come in addition to the regular syntax: import { Http, Get, AuthLevel, FunctionContext } from '@azure/functions';
@Http('/hello')
@Get()
@AuthLevel('anonymous')
export async function ({ req, res }: FunctionContext) {
res.body = 'Hello World!';
} One key point is see would be to provide sensible defaults to reduce boilerplate (as @aaronpowell said), like in my example above the Such an approach regarding default would also work with plain JS using functions: import { app, FunctionContext } from '@azure/functions';
app.http('/hello', { methods: ['get'], authLevel: 'anonymous' },
async ({ req, res }: FunctionContext) => {
res.body = 'Hello World!';
}); Or with a fluent api: import { app, FunctionContext } from '@azure/functions';
app.http('/hello')
.methods(['get'])
.authLevel('anonymous')
.register({ req, res }: FunctionContext) => {
res.body = 'Hello World!';
}); Of course, the full verbose would still be accessible, but reducing the amount of boilerplate for common use cases is something to not be overlooked. |
I'm going to disagree with this. From the conversations that I've had with the TypeScript team, I felt that they were not really encouraging new projects to use the decorators that are in TypeScript because, as you pointed out, they aren't in line with the spec and if/when decorators do make it into the spec, they won't be able to directly port over the current implementation, so they'll have to have two different implementations within the language itself. |
The first goal is to be idiomatic for Node.js dev, and the fact is that decorators are hugely popular in all TS-first Node.js libs. This is what I think is the most important point here. Now, I don't say that the API should be built exclusively around decorators, but we can use them as syntactic sugar around the base API. When/if the new decorators spec will be defined, the whole TS ecosystem would have to think about a migration path, and the TS compiler won't drop support for the old spec the next day. |
I understand there may be technical reasons decorators would be difficult for us (as @aaronpowell said), but I do plan to explore that option and have at least one prototype with decorators. I make no promises, but I think it's worth considering |
I support exploring the decorators option. Makes a lot of sense to me. |
With decorators progressing to Stage 3 last week, it's probably more viable to consider, but we'd want to leverage the design of decorators that's spec'ed out rather than the one in TypeScript today (and that might make things harder). |
Wouldn't TS decorators be just a nice potential feature of the new programming model? Imo it sounds like a good compromise to keep this feature in mind and focus on getting the basics of a good programming model right. I like the brevity of the high-level API form suggested here by @sinedied, leveraging the low-level API proposal of @aaronpowell. import { app, FunctionContext } from '@azure/functions';
app.http('/hello', { methods: ['get'], authLevel: 'anonymous' },
async ({ req, res }: FunctionContext) => {
res.body = 'Hello World!';
}); |
I like the idea of decorators in principle but until there's support for the proposal that's going forward not the legacy one in TypeScript and Babel/swc/etc. I see it as being a really hard thing to support as a first-class citizen. |
A first pass that includes support for http, timer, and storage, plus a 'generic' option that could be used for anything. One of the biggest pieces missing is that this does not touch the http request or response types yet. Related to Azure/azure-functions-nodejs-worker#480
@aaronpowell IMO the object literal approach is where you should start. Once you have that, community members interested in decorators could roll their own package. Allowing functions to be registered via code really opens the door to a lot of potential solutions. IMO, there was only support for fluent API, it makes it a bit harder to extend. |
Hi folks! We've actually narrowed it down to one option, and it's a working prototype and everything. More details here if you're interested in giving some feedback: https://aka.ms/AzFuncNodeV4 @ChuckJonas in terms of decorators, it's definitely been on the back burner for now. We want to prototype it still, but we've been focused on a non-decorators approach as described in the link above. |
@ejizba just gave it a review. I like where this is going! Honestly the thing I'm most excited about is just being able to specify whatever folder structure fits the project best and not have every function as a folder in the root. Our project is using OpenAPI specs to define our API contracts along side each function. We use Overall, I think it's a pretty nice approach, but the limitations around function registration really make it harder to maintain than it would otherwise be. Some feedback below: Define function in code
Simplified context, inputs, and outputs
That will be very welcome. Been burned by missing the context duplication in our test setup a couple times now. Are there any plans to improve Service Bus IN/OUT formats? Guessing that might be outside the scope of this project, but I have two major problems with how service bus bindings work in AZ Function:
Create a test contextWill be nice to have this out of the box... We basically rolled out our version of something very similar in our current project. Question: Rules around function registrationJust curious in general how the function registration works...
PS: |
@ChuckJonas thanks for the feedback! I think I've covered everything:
It's the function name. Created this issue to continue the discussion on this: Azure/azure-functions-nodejs-library#17
Yeah here are my example functions with a few service bus ones: https://github.com/ejizba/func-nodejs-prototype/tree/main/src/functions. Make sure to read the readme of that repo if you want to know how to try it out
No there are not. Agreed it's probably out of scope, as I think your problems are common across all languages, right? Regardless, feel free to file a separate issue if you want us to follow up
Yes, for example I added some conditions to only register a function if the respective environment variable is set in my example prototype. (see here)
That is not possible. If you try it, you will get an error like "A function can only be registered during app startup."
I've created a separate issue to start tracking any decorators work/discussion: Azure/azure-functions-nodejs-library#18 |
@ejizba Ran into an issue today with distributed tracing that I was wondering if you guys would be solving via this initiative. The problem is outlined here. IDK if maybe this could be solved with the new hooks. I am using appinsights wrapped context, but you end up needing to rewrite all the export default async function contextPropagatingHttpTrigger(context, req) {
// overwrite with the proper traceparent from the incoming SB message.
const sbTraceParent = context.bindingData.applicationProperties['diagnostic-Id'];
if (sbTraceParent) {
context.traceContext.traceparent = sbTraceParent;
}
const correlationContext = appInsights.startOperation(context, req) as CorrelationContext;
// Wrap the Function runtime with correlationContext
return appInsights.wrapWithCorrelationContext(async () => {
const startTime = Date.now(); // Start trackRequest timer
try {
// operation_Id matches SB 'diagnostic-Id'
appInsights.defaultClient.trackTrace({
message: 'Correct Trace Context',
});
// operation_Id is the original function traceparent
context.log('Incorrect Trace Context');
return await trigger(context, req);
} catch (e) {
context.log.error(e);
throw e;
} finally {
// Track Request on completion
appInsights.defaultClient.flush();
}
}, correlationContext)();
} |
We're in public preview! 🎉 Try it out now and let us know what you think - read the blog post here: https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-functions-version-4-of-the-node-js-programming-model-is-in/ba-p/3773541 I'm going to close this issue and we will track any GA work with its own individual issue |
Goals
Plan of action
Moved to here: https://github.com/Azure/azure-functions-nodejs-worker/wiki/Roadmap#new-programming-model
Prototypes
See this repo for the example prototype: https://github.com/ejizba/func-nodejs-prototype
The text was updated successfully, but these errors were encountered: