- Notifications
You must be signed in to change notification settings - Fork0
A showcase for effect errors reporting
License
NotificationsYou must be signed in to change notification settings
jpb06/remix-effect-errors
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Toying withremix andeffect to get some fancy errors reporting usingeffect-errors.
We basically need two things on remix to achieve our goal:
- A custom remix loader accepting an effect and throwing effect errors details.
- An Error boundary to display that information if an error occurs.
importtype{LoaderFunctionArgs}from'@remix-run/server-runtime';import{Effect,pipe}from'effect';import{collectErrorDetails}from'./logic/collect-error-details';import{remixThrow}from'./logic/remix-throw';exportconsteffectLoader=<A,E>(effect:(args:LoaderFunctionArgs)=>Effect.Effect<A,E>)=>async(args:LoaderFunctionArgs)=>awaitEffect.runPromise(pipe(effect(args),Effect.map((data)=>({_tag:'success'asconst, data})),Effect.sandbox,Effect.catchAll(collectErrorDetails),),).then(remixThrow);
If the effect fails, we retrieve errors data and related code:
- In dev mode, effect-errors will use sourcemaps to extract code excerpts related to the error.
- In production however, we must fetch the map file (uploaded in our example on cloudflare R2), and read it to extract sources.
exportconstcollectErrorDetails=<E>(cause:Cause<E>)=>pipe(Effect.gen(function*(){// Serverside loggingconsterrorsText=prettyPrint(cause,{stripCwd:false});console.error(errorsText);const{ errors}=yield*captureErrors(cause,{});if(errors.every((e)=>e.location!==undefined)){// Fetch map file and resolve sourcemaps ...consterrorsWithSources=yield*getErrorSourcesFromMapFile(errors);returnyield*Effect.succeed({_tag:'effect-post-mapped-errors'asconst,errors:errorsWithSources,});}// in Dev mode, sources are resolved by effect-errorsreturnyield*Effect.succeed({_tag:'effect-natively-mapped-errors'asconst, errors,});}),Effect.scoped,Effect.provide(FetchHttpClient.layer),Effect.withSpan('collect-error-details'),);
We need to pipe on the promise because remix expects us to throw ajson
function result from the loader for errors:
import{json}from'@remix-run/server-runtime';import{Match}from'effect';importtype{EffectLoaderError,EffectLoaderSuccess,}from'../types/effect-loader.types';typeRemixThrowInput<A>=EffectLoaderSuccess<A>|EffectLoaderError;consteffectHasSucceeded=<A>(p:RemixThrowInput<A>,):p isEffectLoaderSuccess<A>=>p._tag==='success';exportconstremixThrow=<A>(input:RemixThrowInput<A>)=>Match.value(input).pipe(Match.when(effectHasSucceeded,({ data})=>data),Match.orElse((data)=>{throwjson(data,{status:500});}),);
First, let's create a hook to get errors data:
import{isRouteErrorResponse,useLocation,useRouteError,}from'@remix-run/react';importtype{EffectNativelyMappedErrors,EffectPostMappedErrors,}from'@server/loader/types/effect-loader.types';import{isUnknownAnEffectError}from'./logic/is-uknown-an-effect-error.logic';import{mapEffectErrorTypes}from'./logic/map-effect-error-types';exporttypeEffectPostMappedErrorsWithPath=EffectPostMappedErrors&{path:string;};exporttypeEffectNativelyMappedErrorsWithPath=EffectNativelyMappedErrors&{path:string;};exporttypeErrorsDetails=|{_tag:'route'|'error'|'unknown';path:string;errors:{message:string;}[];}|EffectPostMappedErrorsWithPath|EffectNativelyMappedErrorsWithPath;exportconstuseErrorDetails=():ErrorsDetails=>{const{ pathname}=useLocation();consterror=useRouteError();if(isUnknownAnEffectError(error)){returnmapEffectErrorTypes(error,pathname);}constisRoute=isRouteErrorResponse(error);if(isRoute){return{_tag:'route'asconst,path:pathname,errors:[{message:`${error.statusText}`,},],};}if(errorinstanceofError){return{_tag:'error'asconst,path:pathname,errors:[error],};}return{_tag:'unknown'asconst,path:pathname,errors:[{message:'Unknown Error'}],};};
We can then focus on displaying our errors data...
import{AppErrors}from'./children/app-errors';import{Summary}from'./children/summary';import{errorBoundaryStyles}from'./error-boundary.styles';import{useErrorDetails}from'./hooks/use-error-details';exportconstErrorBoundary=()=>{constcss=errorBoundaryStyles();constdata=useErrorDetails();return(<divclassName={css.root}><Summary{...data}/><AppErrors{...data}/></div>);};
About
A showcase for effect errors reporting
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
No releases published
Packages0
No packages published
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.