:wave: I was looking at <https://www.apollographql...
# apollo-kotlin
w
👋 I was looking at https://www.apollographql.com/docs/kotlin/advanced/nullability#migrate-from-nonnull and wanted to try the new nullability annotations. However, as it happens we're currently using the
responseBased
codegen, and I've got an error that
Apollo: responseBased codegen does not support @catch
. I'm not too familiar with the current nullability handling state, so just wanted to get some clarity on what to expect moving forward — I see there are some limitations to the
responseBased
codegen listed here, but nullability is not there
m
Hi 👋 Sorry for the delay, just back from GraphQLConf where nullability was one of the big topics being discussed. When the talks are out (~couple of weeks), I recommend watching https://graphql.org/conf/2025/schedule/4ed67778faddda05ce0a191e525d43ee/ and https://graphql.org/conf/2025/schedule/bca05d46cfc531aeb3cd84927f6483c1/
thank you color 1
Back to
responseBased
codegen, my current idea is that we add features there once they have been proven successful on the
operationBased
codegen. Maintaining the experimental features in both codegen is going to be a lot
In terms of setting up expectation, this has been going on in the GraphQL community for almost 5 years now so while I'm hopeful that we see the end of the tunnel, I have been burnt before so it may take a while for all of this to become stable
w
Hi thanks, that makes sense! About expectations I mostly meant if
responseBased
codegen will remain a first-class citizen as nullability evolves, or we should better plan to migrate to operations based (or the new experimental one with interfaces) instead
And also I hear that maybe migrating to the new nullability annotations isn't necessarily future-proof yet 😄
A small ask then — would it be ok to stop reporting warnings on
@nonnull
annotations if
responseBased
codegen is used? Since there's no migration path for them, and
reponseBased
itself isn't deprecated
m
Ah, yes, that's a good catch (no pun intended)
But then I'd really like to get rid of
@nonnull
as
@semanticNonNull
is usually much better
Let me see how much of an effort it is to bring it to
responseBased
codegen
👀 1
We already have 2 codegen, I'm not thrilled by the idea of having a 2x2 matrix (codegen x nullability annotation)
w
sounds good! And then to keep exactly the same behavior as
@nonnull
, I'd do
@catchByDefault(to: NULL)
in the schema extension first (that's a no op) and then replace all the
@nonnull
in operations with
@catch(to: THROW)
?
m
Yes, exactly
🙇 1
I'll update this thread today or tomorrow depending the roadblock (or absence of) for bringing
@semanticNonNull
to
responseBased
codegen
w
thanks! and let me know if I can help! I don't know too much about the codegen but if it's possible to explain what needs to be done I'm happy to work on it (though I'm not very familiar with Apollo's codegen code)
kodee loving 1
m
Thanks! Let me see how deep that goes, I'm not sure myself what's needed here 😅
🤞 1
It ended up being not too bad. 2 things though: 1.
responseBased
is not a great contender for
@catch
because merged field are required to have the same
@catch
directives. This is probably slow to validate and breaks fragment locality 2. I realized there's no strict equivalent to
@nonnull
when used in queries.
@semanticNonNull
works only in schemas
Let me know how that works for you
w
merged field are required to have the same
@catch
directives. This is probably slow to validate and breaks fragment locality
this means that if I'm fetching the same field in multiple places, I need to declare the same
@catch
directive on each instance of the same field? I don't think that's too bad, but just so I understand — it means that I can't have the same fragment used in two queries where the nullability behavior is different, right? E.g. I know in query A field
foo/bar
will not be null, but in query
B
the same field might be null because it's in a different context, I can't use the same fragment for both queries?
I realized there's no strict equivalent to @nonnull when used in queries. @semanticNonNull works only in schemas
this one I'm not sure I understand. I still can use
@catch(to: NULL)
in executable documents/queries, but I need to define each field as
@semanticNonNullField
first? So basically for each field I need at least 1 extension in the schema, and then a
@catch
on each usage?
m
it means that I can’t have the same fragment used in two queries where the nullability behavior is different, right?
yup, exactly that
basically for each field I need at least 1 extension in the schema, and then a
@catch
on each usage?
also correct. One
@semanticNonNull
for the field definition, one
@catch(to: THROW)
for fields that you want to be generated as non-null types
w
And the second point is different compared to
operationBased
in that in
operationBased
I would opt-in to catch annotations globally and then be able to
catch
fields in operations, no need for schema extensions?
m
not really,
operationBased
and
responseBased
are really similar
operationBased
also needs a schema extension
the reasoning is that • data definition belongs to the schema • error handling belongs to the client
So if a given field is semantically non-null for a query, it’s probably semantically non-null everywhere
This whole idea started as “CCN” (Client Controlled Nullability) but in fact the client cannot control the nullability really, it’s a schema concern
w
Right, it all make sense, although — I'm not following the discussions mind you — it does seem redundant? Like if I do a
@catch
on a field in a query, that field • must be nullable in the schema • can be inferred as semantically non null (otherwise I wouldn't
@catch
it) • it would make some sense to default to
@catch(to: NULL)
for other usages of that field that don't have an explicit catch
actually sorry, I think I'm just not sure what the differences between
operationBased
and
responseBased
nullability handling would be, after the PR you opened. As a client, in queries/schema, do I do anything differently between the two codegens?
m
• must be nullable in the schema
Right, it must be nullable in the schema as in either
foo: Int
or
foo: Int @semanticNonNull
because there will never be an error for
foo: Int!
• can be inferred as semantically non null (otherwise I wouldn’t
@catch
it)
Not really. If you have
title: Int
(regular nullable, i.e. a video may not have a title), you may still want to
@catch(to: RESULT)
to handle that error
👍 1
🧠 1
• it would make some sense to default to
@catch(to: NULL)
for other usages of that field that don’t have an explicit catch
I think
@catchByDefault(to: NULL)
does that?
You have to opt-in a default
@catchByDefault
@catchByDefault(to: THROW)
is probably the most correct one.
@catchByDefault(to: NULL)
is the compatible one
I’m hoping in some future, every schema becomes a
@catchByDefault(to: THROW)
differences between
operationBased
and
responseBased
nullability handling would be, after the PR you opened. As a client, in queries/schema, do I do anything differently between the two codegens?
Mostly the same thing
I was just too lazy to do it for
responseBased
at the time but it turned out to be quite simple
w
the compatible one
as in, the one that no-ops compared to the schema/queries without any nullability annotation
👍 1
m
yes, that
w
> Mostly the same thing That's what I was confused about I think, I thought the
responseBased
does something different. But the only difference is the additional restrictions about having to match the
@catch
annotation for fields fetched multiple times, the thing you mentioned earlier > merged field are required to have the same
@catch
directives.
m
Yes, sorry, it’s the same for both except you get a lot more merged fields with
responseBased
so it will hapen probably more
Copy code
{
  ...queryDetails
  foo @catch(to: NULL)
}
fragment queryDetails on Query {
  foo: @catch(to: RESULT)
}
This ☝️ is going to fail for
responseBased
while it’s ok for
operationBased
Because each fragment has its own model in
operationBased
codegen
w
Yep got it! I think it's all clear then, thanks for your patience 😄 Once the PR is out I'll see how the migration goes, hopefully we don't merge fields too much for the current usages of
@nonnull
🙂
m
Sure thing, I’m really curious how that work for you, please let me know
w
no promises but if I have some time this/next week I'll try to test a snapshot
💙 1
m
Ah yes, I wanted to ask, do you need this in 4.x?
It can probably be backported
w
Honestly I don't really need it, I'm just cracking down on warnings in build output and that was a big offender 😄 We are on a pretty old version though (4.2.0), not sure if there was a reason for that or bump was just not prioritized
m
Alright, I’ll sleep on it but I will probably end up backporting it so that users of 4.x have a clear path forward
w
I haven't checked out 5.x yet btw so I don't even know if anything breaks there for us
But whatever makes sense from your end, worst case I'll disable warnings for now and address them once we migrate to 5.x
👍 1
m
5.x should be relatively boring. It’s mostly about removing some old things.
😌 1