Skip to content
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

Background sync does not work #739

Open
VityaSchel opened this issue Aug 12, 2024 · 6 comments
Open

Background sync does not work #739

VityaSchel opened this issue Aug 12, 2024 · 6 comments

Comments

@VityaSchel
Copy link

My goal is to have PWA that can work offline and online with Vite. I've added vite-plugin-pwa and configured background sync as follows:

workbox: {
  maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
  runtimeCaching: [
    {
      urlPattern: /\/api\/.*$/,
      handler: 'NetworkFirst',
      method: 'POST',
      options: {
        backgroundSync: {
          name: 'api-queue',
          options: {
            maxRetentionTime: 72 * 60
          }
        }
      }
    },
    {
      urlPattern: /\/api\/.*$/,
      handler: 'NetworkFirst',
      method: 'PATCH',
      options: {
        backgroundSync: {
          name: 'api-queue',
          options: {
            maxRetentionTime: 72 * 60
          }
        }
      }
    },
    {
      urlPattern: /\/api\/.*$/,
      handler: 'NetworkFirst',
      method: 'DELETE',
      options: {
        backgroundSync: {
          name: 'api-queue',
          options: {
            maxRetentionTime: 72 * 60
          }
        }
      }
    },
  ]
}

Now when I make fetch request to my server when offline I expect it to immediately reject with some kind of error and add to that indexeddb queue with name 'api-queue' and automatically retry when browser is online within period of 72 hours

This is my wrapper function for making api request:

export async function changeAvatar({ userId, avatar }) {
  try {
    await fetch(apiUrl + '/user', {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        type: 'avatar',
        userId,
        avatar
      })
    })
  } catch {
    if(navigator.onLine) {
      throw new Error('Failed to change avatar')
    } else {
      toast.info('Changes will be synced when you come back online')
    }
  }
  // some calls that change avatar in local database so that user sees changes instantly
}

Which should either throw error (if user is online, for example if api is down) or show 'Changes will be synced when you come back online' toast when user is offline indicating that service worker added request to queue

However most of the times the request is simply stuck in network tab until it timeouts in like 1 minute, and promise never neither rejects nor fulfills. Other tiems it just instantly rejects with some error like NS_ERROR_UNKNOWN_HOST and correctly shows toast about pending changes. However after I connect to internet again, no requests to network are made in network tab.

More importantly, in neither of these cases, nothing is added to indexeddb workbox-background-sync which should hold requests until they fulfilled

os: macos 14.5
browser: firefox 129.0

@userquin
Copy link
Member

userquin commented Aug 12, 2024

Try replacing /\/api\/.*$/ with /^\/api\// adding it to workbox.navigateFallbackDenylist regex array: you need to exclude sw precaching handler to intercept any /api/ request.

You can also add internal plugins to the handlers, check sveltekit linked comment issue.

@VityaSchel
Copy link
Author

I just realized Firefox does not support this API at all
I tried it in chrome however and it looks like it still does not add anything to background sync tab of application tab
fetch rejects immediately and toast is displayed correctly, but I don't see anything added to background sync tab or indexed db or cache storage

@userquin
Copy link
Member

can you share the repo if public?

@graphographer
Copy link

I haven't yet gotten background sync to work either, but I do know that you should be getting errors because you have multiple configurations that use the same name. If you need to apply the background sync to multiple methods, I think you have make urlPattern a callback function,. Then you can check the method property on the event that's passed into it, in addition to any url regex.

I'm about to try userquin's advice and I'll report back here.

@graphographer
Copy link

I'm unable to get anything at all w/r/t background sync. I'm stopping my dev server(s) and have even tried turning off my WiFi. Typically I create an entity using a POST command. Then I turn off my dev server/wifi and issue the DELETE command from the app. Nothing turns up in DevTools under IndexedDB or Background sync.

Here's my Vite PWA configuration:

VitePWA({
	registerType: 'autoUpdate',
	injectRegister: false,
	injectManifest: {
		injectionPoint: undefined
	},
	devOptions: {
		enabled: true
	},
	manifest: {
		name: '...',
		theme_color: '#ffffff',
		background_color: '#ffffff',
		start_url: '/',
		display: 'standalone',
		prefer_related_applications: false,
		icons: [
			{
				src: 'pwa-64x64.png',
				sizes: '64x64',
				type: 'image/png'
			},
			{
				src: 'pwa-192x192.png',
				sizes: '192x192',
				type: 'image/png'
			},
			{
				src: 'pwa-512x512.png',
				sizes: '512x512',
				type: 'image/png',
				purpose: 'any'
			},
			{
				src: 'maskable-icon-512x512.png',
				sizes: '512x512',
				type: 'image/png',
				purpose: 'maskable'
			}
		]
	},
	workbox: {
		globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
		sourcemap: true,
		navigateFallback: null,
		navigateFallbackDenylist: [/^\/api\//],
		runtimeCaching: [
			{
				handler: 'CacheFirst',
				urlPattern: e => {
					return e.url.toString().match(/\.(jpg|jpeg|gif|png)/);
				},
				options: {
					cacheName: 'images-cache',
					cacheableResponse: {
						statuses: [0, 200]
					}
				}
			},
			{
				handler: 'NetworkOnly',
				urlPattern: e => {
					const { method, url } = e.request;

					if (method !== 'POST' && method !== 'DELETE') {
						return false;
					}

					return url.toString().match(/^\/api\//);
				},
				options: {
					backgroundSync: {
						name: 'backgroundSyncQueue',
						options: {
							maxRetentionTime: 24 * 60
						}
					}
				}
			}
		]
		// disableDevLogs: true
	},
	pwaAssets: {
		config: true,
		overrideManifestIcons: true
	}
})

@graphographer
Copy link

graphographer commented Sep 11, 2024

After searching and tinkering, I came across this github issue: GoogleChrome/workbox#2393

My tests indicate that this is pivotal bit to get background sync working, at least when running things from the Vite dev server.

If they are trying to test this feature using the dev server, my recommendation for @VityaSchel then, is to simply add forceSyncFallback: true to the options, and to make each name for each http method config have unique names.

For example

{
  handler: 'NetworkOnly',
  method: 'PUT',
  urlPattern: /\/api\//,
  options: {
	  backgroundSync: {
		  name: 'bg-sync-queue-PUT',
		  options: {
			  maxRetentionTime: 24 * 60,
			  forceSyncFallback: true
		  }
	  }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants